home *** CD-ROM | disk | FTP | other *** search
/ Giga Games 1 / Giga Games.iso / net / usenet / volume6 / cubes2 / part06 < prev    next >
Encoding:
Internet Message Format  |  1989-07-06  |  54.6 KB

  1. Path: uunet!tektronix!tekgen!tekred!games
  2. From: games@tekred.CNA.TEK.COM
  3. Newsgroups: comp.sources.games
  4. Subject: v06i064:  cubes2 - a networked dice game (version 5.1), Part06/08
  5. Message-ID: <3905@tekred.CNA.TEK.COM>
  6. Date: 27 Apr 89 19:23:29 GMT
  7. Sender: billr@tekred.CNA.TEK.COM
  8. Lines: 2382
  9. Approved: billr@saab.CNA.TEK.COM
  10.  
  11. Submitted-by: gmp@rayssdb.RAY.COM (Gregory M. Paris)
  12. Posting-number: Volume 6, Issue 64
  13. Archive-name: cubes2/Part06
  14.  
  15.  
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then unpack
  19. # it by saving it into a file and typing "sh file".  To overwrite existing
  20. # files, type "sh file -c".  You can also feed this as standard input via
  21. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  22. # will see the following message at the end:
  23. #        "End of archive 6 (of 8)."
  24. # Contents:  actions.c cubeserv2.c history.c
  25. # Wrapped by billr@saab on Thu Apr 27 12:13:39 1989
  26. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  27. if test -f 'actions.c' -a "${1}" != "-c" ; then 
  28.   echo shar: Will not clobber existing file \"'actions.c'\"
  29. else
  30. echo shar: Extracting \"'actions.c'\" \(15195 characters\)
  31. sed "s/^X//" >'actions.c' <<'END_OF_FILE'
  32. X/*    vi:set sw=4 ts=4: */
  33. X#ifndef    lint
  34. Xstatic char    sccsid[] = "@(#)actions.c 5.1 (G.M. Paris) 89/01/22";
  35. X#endif    lint
  36. X
  37. X/*
  38. X**
  39. X**    cubes 5.1  Copyright 1989 Gregory M. Paris
  40. X**        Permission granted to redistribute on a no charge basis.
  41. X**        All other rights are reserved.
  42. X**
  43. X*/
  44. X
  45. X#include    <stdio.h>
  46. X#include    <signal.h>
  47. X#include    <strings.h>
  48. X#include    <ctype.h>
  49. X#include    <sys/types.h>
  50. X#include    <sys/time.h>
  51. X#include    <sys/file.h>
  52. X#include    <sys/socket.h>
  53. X#include    <sys/ioctl.h>
  54. X#include    <netdb.h>
  55. X#include    <netinet/in.h>
  56. X#include    <pwd.h>
  57. X#include    "cubes.h"
  58. X
  59. Xplayer                plr[PLAYERS];        /* player status */
  60. Xdiceset                dice;                /* the status of the dice */
  61. Xcstat                mystat    = Inactive;    /* our playing status */
  62. Xboolean                jokermode= False;    /* jokers on the dice */
  63. Xboolean                inprogress= False;    /* game in progress */
  64. Xint                    winscore= 0;        /* game win score */
  65. Xint                    mypnum    = -1;        /* my player number */
  66. Xint                    turnnum    = 0;        /* current turn number */
  67. Xint                    nobs    = 0;        /* number of observers */
  68. Xint                    beats    = 0;        /* number of heartbeats */
  69. Xextern boolean        quiet;                /* in background */
  70. Xextern enterlate    watch;                /* what to do about late entry */
  71. Xextern enterlate    spider;                /* what to do about waiting */
  72. Xextern winpref        preference;            /* gametype/winscore preference */
  73. Xextern char           *cubename;            /* player name */
  74. Xextern char           *cubehist;            /* personal ranking history file */
  75. Xextern int            ssock;                /* server socket */
  76. X
  77. Xextern int            errno;
  78. Xextern char           *sys_errlist[];
  79. Xextern boolean        active;                /* True while active */
  80. Xextern char           *index();
  81. Xextern char           *getenv();
  82. Xstruct passwd       *getpwuid();
  83. X
  84. X/*
  85. X**    respond: send a reply to the server adding "\r\n"
  86. X*/
  87. Xrespond(reply)
  88. Xchar   *reply;
  89. X{
  90. X    char    repbuf[BUFSIZ];
  91. X
  92. X    strcpy(repbuf, reply);
  93. X    strcat(repbuf, "\r\n");
  94. X
  95. X    return write(ssock, repbuf, strlen(repbuf)) < 0 ? -1 : 0;
  96. X}
  97. X
  98. X/*
  99. X**    dispact: display an informative message to the player
  100. X*/
  101. X/*ARGSUSED*/
  102. Xdispact(t,m)
  103. Xchar *m;
  104. X{
  105. X    return infomesg(m+4);
  106. X}
  107. X
  108. X/*
  109. X**    heloact: respond to the M_HELO message by giving player name.
  110. X*/
  111. X/*ARGSUSED*/
  112. Xheloact(t,m)
  113. Xchar *m;
  114. X{
  115. X    return respond(cubename);
  116. X}
  117. X
  118. X/*
  119. X**    rqidact: respond to the M_RQID message by giving player identity
  120. X**        and gametype preference.
  121. X*/
  122. X/*ARGSUSED*/
  123. Xrqidact(t,m)
  124. Xchar *m;
  125. X{
  126. X    char            idbuf[IDLEN];
  127. X    char            host[256];
  128. X    int                uid;
  129. X    struct passwd  *pw;
  130. X    char            pchar;
  131. X
  132. X    (void) gethostname(host, sizeof host);
  133. X
  134. X    switch(preference) {
  135. X    case Standard:    pchar = 's'; break;
  136. X    case Fstand:    pchar = 'S'; break;
  137. X    case Blitz:        pchar = 'b'; break;
  138. X    case Fblitz:    pchar = 'B'; break;
  139. X    default:        pchar = 'n'; break;
  140. X    }
  141. X
  142. X    /*
  143. X    **    Look up login name in the password file.  If it's not there,
  144. X    **    just use the user id number.
  145. X    */
  146. X    uid = getuid();
  147. X    if((pw = getpwuid(uid)) == 0)
  148. X        sprintf(idbuf, "%c;%d@%.*s", pchar, uid, IDLEN-12, host);
  149. X    else
  150. X        sprintf(idbuf, "%c;%s@%.*s", pchar, pw->pw_name, IDLEN-12, host);
  151. X
  152. X    return respond(idbuf);
  153. X}
  154. X
  155. X/*
  156. X**    worpact: respond to the M_WORP message by saying play or watch
  157. X*/
  158. Xworpact(t,m)
  159. Xchar *m;
  160. X{
  161. X    switch(watch) {
  162. X    case Watch:    return respond("watch");
  163. X    case Play:    return respond("play");
  164. X    default:    return rfstact(t,m);
  165. X    }
  166. X}
  167. X
  168. X/*
  169. X**    sorpact: respond to the M_SORP message by saying wait or play
  170. X*/
  171. Xsorpact(t,m)
  172. Xchar *m;
  173. X{
  174. X    switch(spider) {
  175. X    case Wait:    return respond("wait");    /* be a spider */
  176. X    case Play:    return respond("play");
  177. X    default:    return rfstact(t,m);
  178. X    }
  179. X}
  180. X
  181. X/*
  182. X**    actvact: respond to the M_ACTV message by alerting the player
  183. X*/
  184. X/*ARGSUSED*/
  185. Xactvact(t,m)
  186. Xchar *m;
  187. X{
  188. X    cstat    oldstat;
  189. X
  190. X    oldstat = mystat;
  191. X    mystat = Active, beats = 0;
  192. X    if(quiet == True)
  193. X        mybeep(2);
  194. X
  195. X    switch(oldstat) {
  196. X    case Computer:
  197. X        (void) infomesg("You've regained control of the dice.");
  198. X        break;
  199. X    case Watching:
  200. X        if(turnnum > 1)
  201. X            (void) infomesg("You've joined the game in progress.");
  202. X        break;
  203. X    default:
  204. X/*        (void) infomesg(m+4);    /* don't need this chatter */
  205. X        break;
  206. X    }
  207. X
  208. X    return 0;
  209. X}
  210. X
  211. X/*
  212. X**    autoact: respond to the M_AUTO message by alerting the player
  213. X*/
  214. X/*ARGSUSED*/
  215. Xautoact(t,m)
  216. Xchar *m;
  217. X{
  218. X    mystat = Computer, beats = 0;
  219. X    (void) infomesg(m+4);
  220. X    return 0;
  221. X}
  222. X
  223. X/*
  224. X**    waitact: respond to the M_WAIT message by notifying the player
  225. X*/
  226. X/*ARGSUSED*/
  227. Xwaitact(t,m)
  228. Xchar *m;
  229. X{
  230. X    if(mystat == Spider || mystat == Watching)
  231. X        (void) infomesg(m+4);
  232. X    mystat = Waiting, beats = 0;
  233. X    return 0;
  234. X}
  235. X
  236. X/*
  237. X**    voyract: respond to the M_VOYR message by notifying the player
  238. X*/
  239. X/*ARGSUSED*/
  240. Xvoyract(t,m)
  241. Xchar *m;
  242. X{
  243. X    mystat = Watching, beats = 0;
  244. X    return infomesg(m+4);
  245. X}
  246. X
  247. X/*
  248. X**    spdract: respond to the M_SPDR message by entering spider mode
  249. X**        We could suspend here, but that makes this mode
  250. X**        unusable in shells without job control.
  251. X*/
  252. X/*ARGSUSED*/
  253. Xspdract(t,m)
  254. Xchar *m;
  255. X{
  256. X    mystat = Spider, beats = 0;
  257. X    (void) infomesg(m+4);
  258. X#ifdef    notdef
  259. X    sleep(2);
  260. X    (void) kill(0, SIGTSTP);    /* suspend process group */
  261. X#endif    notdef
  262. X    return 0;
  263. X}
  264. X
  265. X/*
  266. X**    noopact: no operation (really heartbeat action)
  267. X**        Assumes that heartbeats come one per minute.
  268. X*/
  269. X/*ARGSUSED*/
  270. Xnoopact(t,m)
  271. Xchar *m;
  272. X{
  273. X    char    msgbuf[MESGLEN];
  274. X
  275. X    ++beats;
  276. X    sprintf(msgbuf, "You've been a spider for %d minute%s...",
  277. X        beats, beats == 1 ? "" : "s");
  278. X    return infomesg(msgbuf);
  279. X}
  280. X
  281. X/*
  282. X**    infoact: display an informational message
  283. X*/
  284. X/*ARGSUSED*/
  285. Xinfoact(t,m)
  286. Xchar *m;
  287. X{
  288. X    return infomesg(m+4);
  289. X}
  290. X
  291. X/*
  292. X**    helpact: display a help message
  293. X*/
  294. X/*ARGSUSED*/
  295. Xhelpact(t,m)
  296. Xchar *m;
  297. X{
  298. X    return helpmesg(m+4);
  299. X}
  300. X
  301. X/*
  302. X**    playact: display a play-by-play message (not used)
  303. X*/
  304. X/*ARGSUSED*/
  305. Xplayact(t,m)
  306. Xchar *m;
  307. X{
  308. X    return 0;
  309. X}
  310. X
  311. X/*
  312. X**    rfstact: respond to "roll first?" message
  313. X*/
  314. X/*ARGSUSED*/
  315. Xrfstact(t,m)
  316. Xchar *m;
  317. X{
  318. X    char    answer[BUFSIZ];
  319. X
  320. X    switch(prompt(m+4, answer, False)) {
  321. X    case -2:    /* timeout */
  322. X        return 0;
  323. X    case -1:    /* error */
  324. X        return -1;
  325. X    }
  326. X    if(respond(answer) < 0)
  327. X        return -1;
  328. X    return 0;
  329. X}
  330. X
  331. X/*
  332. X**    keepact: respond to "%d points:" message
  333. X*/
  334. Xkeepact(t,m)
  335. Xchar *m;
  336. X{
  337. X    return rfstact(t,m);
  338. X}
  339. X
  340. X/*
  341. X**    anogact: respond to "another game?" message
  342. X*/
  343. Xanogact(t,m)
  344. Xchar *m;
  345. X{
  346. X    return rfstact(t,m);
  347. X}
  348. X
  349. X/*
  350. X**    downact: print the shutdown message and exit gracefully
  351. X*/
  352. X/*ARGSUSED*/
  353. Xdownact(t,m)
  354. Xchar *m;
  355. X{
  356. X    for(m += 4;*m == ' ';++m)
  357. X        ;
  358. X    if(*m == '\0')
  359. X        m = "Server said goodbye (no reason given).";
  360. X    (void) infomesg(m);
  361. X    sleep(2);
  362. X    cleanup(0, "");
  363. X    return -1;
  364. X}
  365. X
  366. X/*
  367. X**    turnact: cause turn indicator to advance
  368. X*/
  369. X/*ARGSUSED*/
  370. Xturnact(t,m)
  371. Xchar *m;
  372. X{
  373. X    int        pnum;
  374. X
  375. X    if(sscanf(m+4, "Turn %d: player %d", &turnnum, &pnum) != 2)
  376. X        return -1;
  377. X    --pnum;
  378. X
  379. X    return nowup(pnum);
  380. X}
  381. X
  382. X/*
  383. X**    cplract: clear player roster
  384. X*/
  385. X/*ARGSUSED*/
  386. Xcplract(t,m)
  387. Xchar *m;
  388. X{
  389. X    register int    c;
  390. X
  391. X    mypnum = -1;
  392. X    for(c = 0;c < PLAYERS;++c) {
  393. X        plr[c].p_stat = Inactive;
  394. X        plr[c].p_name[0] = '\0';
  395. X        plr[c].p_score = plr[c].p_squander = 0;
  396. X    }
  397. X
  398. X    return clearscores();
  399. X}
  400. X
  401. X/*
  402. X**    uareact: setup this player
  403. X*/
  404. X/*ARGSUSED*/
  405. Xuareact(t,m)
  406. Xchar *m;
  407. X{
  408. X    int        pnum;
  409. X    char   *s;
  410. X
  411. X    if(sscanf(m+4, "You are player %d", &pnum) < 1)
  412. X        return -1;
  413. X    --pnum;
  414. X
  415. X    mypnum = pnum;
  416. X    plr[pnum].p_score = plr[pnum].p_squander = 0;
  417. X    plr[pnum].p_stat = Active;
  418. X    plr[pnum].p_name[0] = '\0';
  419. X    if((s = index(m+20, ' ')) == 0)    /* mmm You are player n */
  420. X        return -1;
  421. X    strncat(plr[pnum].p_name, s+1, sizeof plr[pnum].p_name);
  422. X
  423. X    return showplr(pnum);
  424. X}
  425. X
  426. X/*
  427. X**    pnumact: setup another player
  428. X*/
  429. X/*ARGSUSED*/
  430. Xpnumact(t,m)
  431. Xchar *m;
  432. X{
  433. X    int        pnum;
  434. X    char   *s;
  435. X
  436. X    if(sscanf(m+4, "player %d", &pnum) < 1)
  437. X        return -1;
  438. X    --pnum;
  439. X
  440. X    plr[pnum].p_score = plr[pnum].p_squander = 0;
  441. X    plr[pnum].p_stat = Active;
  442. X    plr[pnum].p_name[0] = '\0';
  443. X    if((s = index(m+12, ' ')) == 0)    /* mmm player n */
  444. X        return -1;
  445. X    strncat(plr[pnum].p_name, s+1, sizeof plr[pnum].p_name);
  446. X
  447. X    return showplr(pnum);
  448. X}
  449. X
  450. X/*
  451. X**    fareact: clean up when a player leaves
  452. X*/
  453. X/*ARGSUSED*/
  454. Xfareact(t,m)
  455. Xchar *m;
  456. X{
  457. X    static struct timeval    halfsec    = { 0L, 500000L };
  458. X    int                        pnum;
  459. X    char                    msgbuf[MESGLEN];
  460. X    char                    name[NAMELEN];
  461. X
  462. X    if(sscanf(m+4, "farewell %d %[^\r\n]", &pnum, name) < 2)
  463. X        return -1;
  464. X    --pnum;
  465. X
  466. X    sprintf(msgbuf, "%s has left the game.", name);
  467. X    plr[pnum].p_score = plr[pnum].p_squander = 0;
  468. X    plr[pnum].p_stat = Inactive;
  469. X    plr[pnum].p_name[0] = '\0';
  470. X    (void) clearplr(pnum);
  471. X    (void) infomesg(msgbuf);
  472. X    (void) select(32, NOSEL, NOSEL, NOSEL, &halfsec);
  473. X
  474. X    return 0;
  475. X}
  476. X
  477. X/*
  478. X**    nobsact: update number of observers
  479. X*/
  480. X/*ARGSUSED*/
  481. Xnobsact(t,m)
  482. Xchar *m;
  483. X{
  484. X    if(sscanf(m+4, "%d", &nobs) < 1)
  485. X        return -1;
  486. X    return 0;
  487. X}
  488. X
  489. X/*
  490. X**    mscoact: update my score
  491. X*/
  492. X/*ARGSUSED*/
  493. Xmscoact(t,m)
  494. Xchar *m;
  495. X{
  496. X    int        score, squander;
  497. X
  498. X    if(sscanf(m+4,
  499. X        "You now have %d points (squandered %d).", &score, &squander) < 2)
  500. X        return -1;
  501. X    if(mypnum < 0)
  502. X        return -1;
  503. X
  504. X    plr[mypnum].p_score = score, plr[mypnum].p_squander = squander;
  505. X    return showplr(mypnum);
  506. X}
  507. X
  508. X/*
  509. X**    oscoact: update other player score
  510. X*/
  511. X/*ARGSUSED*/
  512. Xoscoact(t,m)
  513. Xchar *m;
  514. X{
  515. X    int        pnum, score, squander;
  516. X
  517. X    if(sscanf(m+4,
  518. X      "Player %d now has %d points (squandered %d).",
  519. X      &pnum, &score, &squander) < 3)
  520. X        return -1;
  521. X    --pnum;
  522. X
  523. X    plr[pnum].p_score = score, plr[pnum].p_squander = squander;
  524. X    return showplr(pnum);
  525. X}
  526. X
  527. X/*
  528. X**    nwin: no winner message
  529. X*/
  530. X/*ARGSUSED*/
  531. Xnwinact(t,m)
  532. Xchar *m;
  533. X{
  534. X    inprogress = False;
  535. X    (void) infomesg(m+4);
  536. X    return cleardice();
  537. X}
  538. X
  539. X/*
  540. X**    overact: game over with winner
  541. X*/
  542. X/*ARGSUSED*/
  543. Xoveract(t,m)
  544. Xchar *m;
  545. X{
  546. X    inprogress = False;
  547. X    (void) infomesg(m+4);
  548. X    return cleardice();
  549. X}
  550. X
  551. X/*
  552. X**    rankact: save ranking info
  553. X*/
  554. X/*ARGSUSED*/
  555. Xrankact(t,m)
  556. Xchar *m;
  557. X{
  558. X    static FILE       *frec    = 0;
  559. X    int                omask;
  560. X
  561. X    /*
  562. X    **    If cubehist is null then we aren't recording history.
  563. X    */
  564. X    if(cubehist == 0 || *cubehist == '\0')
  565. X        return 0;
  566. X
  567. X    /*
  568. X    **    Open the cubehist file for append.
  569. X    */
  570. X    if(frec == 0) {
  571. X        omask = umask(022);
  572. X        (void) umask(omask|022);    /* ensure write protection */
  573. X        if((frec = fopen(cubehist, "a")) == 0) {
  574. X            char    err[MESGLEN];
  575. X            sprintf(err, "Can't open %s: %s.", cubehist, sys_errlist[errno]);
  576. X            (void) umask(omask);
  577. X            return infomesg(err);
  578. X        }
  579. X        (void) umask(omask);
  580. X    }
  581. X
  582. X    /*
  583. X    **    Write the info supplied by the server.
  584. X    */
  585. X    fprintf(frec, "%s\n", m+4);
  586. X    (void) fflush(frec);
  587. X
  588. X    return 0;
  589. X}
  590. X
  591. X/*
  592. X**    diceact: parse the dice status message and redisplay the dice
  593. X*/
  594. X/*ARGSUSED*/
  595. Xdiceact(t,m)
  596. Xchar *m;
  597. X{
  598. X    register int    d;
  599. X    char            stats[NDICE+1];
  600. X    char            faces[NDICE+1];
  601. X    char            combs[NDICE+1];
  602. X    int                c;
  603. X
  604. X    dice.d_mesg[0] = '\0';
  605. X    if(sscanf(m+4, "%d %s %s %s %d %d %d %d%*1c%[^\n]",
  606. X      &c, stats, faces, combs,
  607. X      &dice.d_pts_roll, &dice.d_pts_dice, &dice.d_pts_turn, &dice.d_pts_max,
  608. X      dice.d_mesg) < 8) {
  609. X        fprintf(stderr, "dieact: couldn't parse dice status\n");
  610. X        return -1;
  611. X    }
  612. X    
  613. X    for(d = 0;d < NDICE;++d) {
  614. X        switch(stats[d]) {
  615. X        case 'f':    dice.d_stat[d] = Free; break;
  616. X        case 'h':    dice.d_stat[d] = Held; break;
  617. X        case 't':    dice.d_stat[d] = Taken; break;
  618. X        case 'r':    dice.d_stat[d] = Rolled; break;
  619. X        default:    dice.d_stat[d] = Free; break;
  620. X        }
  621. X        dice.d_face[d] = isdigit(faces[d]) ? (faces[d] - '0') : BADFACE;
  622. X        switch(combs[d]) {
  623. X        case 'p':    dice.d_comb[d] = Previous; break;
  624. X        case 'n':    dice.d_comb[d] = Nothing; break;
  625. X        case 'j':    dice.d_comb[d] = Joker; break;
  626. X        case '5':    dice.d_comb[d] = Five; break;
  627. X        case '1':    dice.d_comb[d] = Ace; break;
  628. X        case 't':    dice.d_comb[d] = Three_of_a_kind; break;
  629. X        case 'l':    dice.d_comb[d] = Small_straight; break;
  630. X        case 'f':    dice.d_comb[d] = Four_of_a_kind; break;
  631. X        case 'b':    dice.d_comb[d] = Asm_straight; break;
  632. X        case 's':    dice.d_comb[d] = Straight; break;
  633. X        case 'a':    dice.d_comb[d] = All_of_a_kind; break;
  634. X        default:    dice.d_comb[d] = Nothing; break;
  635. X        }
  636. X    }
  637. X
  638. X    return drawdice();
  639. X}
  640. X
  641. X/*
  642. X**    parmact: read game parameters winscore and jokermode
  643. X*/
  644. X/*ARGSUSED*/
  645. Xparmact(t,m)
  646. Xchar *m;
  647. X{
  648. X    int        n;
  649. X    char    word[64];
  650. X
  651. X    if((n = sscanf(m+4, "This is a %d point game with %s",
  652. X      &winscore, word)) < 1)
  653. X        return -1;
  654. X    
  655. X    /*
  656. X    **    This message happens at game startup.  If we're suspended
  657. X    **    we want to alert the player, so send a bunch of beeps.
  658. X    */
  659. X    if(quiet == True)
  660. X        mybeep(3);
  661. X    
  662. X    inprogress = True;
  663. X    jokermode = (n == 2 && word[0] == 'j') ? True : False;
  664. X
  665. X    (void) infomesg(m+4);
  666. X    (void) defhelpmesg();
  667. X    return drawdice();
  668. X}
  669. X
  670. X/*
  671. X**    argeact: argument error, display error message
  672. X*/
  673. X/*ARGSUSED*/
  674. Xargeact(t,m)
  675. Xchar *m;
  676. X{
  677. X    if(quiet != True)
  678. X        mybeep(1);
  679. X    (void) infomesg(m+4);
  680. X    sleep(2);
  681. X    return 0;
  682. X}
  683. X
  684. X/*
  685. X**    badmact: alert player to bad message
  686. X*/
  687. Xbadmact(t,m)
  688. Xchar *m;
  689. X{
  690. X    return (dispact(t,"Bad message follows:") < 0 || dispact(t,m) < 0) ? -1 : 0;
  691. X}
  692. X
  693. X/*
  694. X**    unktact: alert player to unknown type message
  695. X*/
  696. Xunktact(t,m)
  697. Xchar *m;
  698. X{
  699. X    return (dispact(t,"Unknown message follows:") < 0 || dispact(t,m) < 0)
  700. X        ? -1 : 0;
  701. X}
  702. X
  703. X/*
  704. X**    actmap: relates message types to actions
  705. X*/
  706. Xtypedef struct {
  707. X    m_num    a_type;            /* message type M_???? */
  708. X    int      (*a_action)();    /* action to take */
  709. X} actmap;
  710. X
  711. X/*
  712. X**    atable: action table
  713. X*/
  714. Xstatic actmap    atable[]    = {
  715. X    { M_NOOP,    noopact },            /* no operation (usually heartbeat) */
  716. X    { M_INFO,    infoact },            /* informational or status messages */
  717. X    { M_HELP,    helpact },            /* help messages */
  718. X    { M_DICE,    diceact },            /* dice status */
  719. X    { M_PARM,    parmact },            /* game parameters */
  720. X    { M_PLAY,    playact },            /* play-by-play on other players */
  721. X    { M_KEEP,    keepact },            /* %d points: */
  722. X    { M_HELO,    heloact },            /* hello message (and name query) */
  723. X    { M_DOWN,    downact },            /* shutdown message */
  724. X    { M_RQID,    rqidact },            /* id request */
  725. X    { M_WORP,    worpact },            /* play or watch? */
  726. X    { M_SORP,    sorpact },            /* wait (spider) or play? */
  727. X    { M_ACTV,    actvact },            /* you are now an active player */
  728. X    { M_AUTO,    autoact },            /* you are on autopilot */
  729. X    { M_WAIT,    waitact },            /* you are waiting to be added */
  730. X    { M_VOYR,    voyract },            /* you are now a voyeur */
  731. X    { M_SPDR,    spdract },            /* you are now a spider */
  732. X    { M_TURN,    turnact },            /* Player %d, ... up with %d points. */
  733. X    { M_NWIN,    nwinact },            /* game over, no winner */
  734. X    { M_OVER,    overact },            /* Player %d has won the game! */
  735. X    { M_RANK,    rankact },            /* player ranking info after game */
  736. X    { M_CPLR,    cplract },            /* clear player roster */
  737. X    { M_UARE,    uareact },            /* you are player %d */
  738. X    { M_PNUM,    pnumact },            /* player %d %s */
  739. X    { M_FARE,    fareact },            /* farewell %d %s */
  740. X    { M_NOBS,    nobsact },            /* %d observer%s */
  741. X    { M_MSCO,    mscoact },            /* You now have %d points. */
  742. X    { M_OSCO,    oscoact },            /* Player %d now has %d points. */
  743. X    { M_ANOG,    anogact },            /* play another game? */
  744. X    { M_RFST,    rfstact },            /* 0 points, roll first? */
  745. X    { M_ARGE,    argeact },            /* argument error, try again */        
  746. X    { M_BADM,    badmact },            /* bad message */
  747. X};
  748. Xstatic int        nactions    = (sizeof atable / sizeof atable[0]);
  749. X
  750. X/*
  751. X**    actlist: array of pointers to actions
  752. X*/
  753. Xstatic int         (**actlist)()    = 0;
  754. X
  755. X/*
  756. X**    initactions: build an array of actions from atable
  757. X*/
  758. Xinitactions()
  759. X{
  760. X    register int    m;
  761. X    unsigned        size;
  762. X    char           *malloc();
  763. X
  764. X    /*
  765. X    **    Just in case we're called more than once.
  766. X    */
  767. X    if(actlist != 0) {
  768. X        free((char *)actlist);
  769. X        actlist = 0;
  770. X    }
  771. X
  772. X    /*
  773. X    **    Get memory for action list.  Initialize each pointer.
  774. X    */
  775. X    size = (unsigned)(M_LAST - M_BASE + 1);
  776. X    if((actlist = (int (**)())malloc(size * sizeof *actlist)) == 0)
  777. X        cleanup(1, "no memory for action list");
  778. X    for(m = 0;m < size;++m)
  779. X        *(actlist+m) = unktact;    /* un-mapped action */
  780. X    
  781. X    /*
  782. X    **    Install the values mapped in atable.
  783. X    */
  784. X    for(m = 0;m < nactions;++m)
  785. X        actlist[(int)atable[m].a_type-M_BASE] = atable[m].a_action;
  786. X}
  787. X
  788. X/*
  789. X**    action: a dispatching function
  790. X*/
  791. Xaction(type, mesg)
  792. Xint        type;
  793. Xchar   *mesg;
  794. X{
  795. X    int        r;
  796. X
  797. X    if(type < M_BASE || type > M_LAST)
  798. X        r = badmact(type, mesg);
  799. X    else
  800. X        r = (*actlist[type-M_BASE])(type, mesg);
  801. X
  802. X    neutral();
  803. X    return r;
  804. X}
  805. END_OF_FILE
  806. if test 15195 -ne `wc -c <'actions.c'`; then
  807.     echo shar: \"'actions.c'\" unpacked with wrong size!
  808. fi
  809. # end of 'actions.c'
  810. fi
  811. if test -f 'cubeserv2.c' -a "${1}" != "-c" ; then 
  812.   echo shar: Will not clobber existing file \"'cubeserv2.c'\"
  813. else
  814. echo shar: Extracting \"'cubeserv2.c'\" \(18633 characters\)
  815. sed "s/^X//" >'cubeserv2.c' <<'END_OF_FILE'
  816. X/*    vi:set sw=4 ts=4: */
  817. X#ifndef    lint
  818. Xstatic char    sccsid[] = "@(#)cubeserv2.c 5.1 (G.M. Paris) 89/04/25";
  819. X#endif    lint
  820. X
  821. X/*
  822. X**
  823. X**    cubes 5.1  Copyright 1989 Gregory M. Paris
  824. X**        Permission granted to redistribute on a no charge basis.
  825. X**        All other rights are reserved.
  826. X**
  827. X*/
  828. X
  829. X#include    <stdio.h>
  830. X#include    <ctype.h>
  831. X#include    <strings.h>
  832. X#include    <syslog.h>
  833. X#include    <sys/types.h>
  834. X#include    <sys/time.h>
  835. X#include    "cubes.h"
  836. X
  837. Xextern long        gamenum;
  838. Xextern char       *optarg;
  839. Xextern char       *fgets();
  840. Xextern char       *gets();
  841. Xextern char       *malloc();
  842. Xextern history *histbyname();
  843. Xextern char       *moniker();
  844. Xextern time_t    time();
  845. Xstruct tm       *localtime();
  846. Xextern boolean    nameinuse();
  847. Xextern boolean    idinuse();
  848. Xextern boolean    isproxy();
  849. Xextern winpref    workhourstype();
  850. X
  851. Xextern unsigned    nhist;                    /* number of entries in history */
  852. Xextern unsigned    neq;                    /* number of equivalent hosts */
  853. Xextern char      **equiv;                    /* equivalent hosts */
  854. Xextern int        active;                    /* number of Active humans */
  855. Xextern int        waiting;                /* number of Waiting humans */
  856. Xextern int        watching;                /* number of Watching humans */
  857. Xextern int        spiders;                /* number of Spiders */
  858. Xextern int        turnnum;                /* current turn number */
  859. Xextern boolean    enajokers;                /* enable jokers */
  860. Xextern boolean    jokermode;                /* dice do not have joker faces */
  861. Xextern boolean    inprogress;                /* True when game in progress */
  862. Xextern boolean    clearobs;                /* Observers are showing in roster */
  863. Xextern blitzmode blitzwhen;                /* Blitz mode enforced during work */
  864. Xextern winpref    gametype;                /* specifies winscore */
  865. Xextern int        winscore;                /* points needed to win */
  866. Xextern player    plr[];                    /* player/connection list */
  867. X
  868. X/*
  869. X**    prescreen: does this person really want to play?
  870. X*/
  871. Xboolean
  872. Xprescreen(c)
  873. Xregister int    c;
  874. X{
  875. X    char   *reason;
  876. X
  877. X    /*
  878. X    **    If there's a game in progress, we'll return False if the
  879. X    **    game mode is incompatible with the player's preference.
  880. X    **    If no game is in progress, we check to see if the prevailing
  881. X    **    mode is enough to decide that the person doesn't want to play.
  882. X    */
  883. X    reason = 0;
  884. X    switch(plr[c].p_pref) {
  885. X    case Fstand:    /* only wants to play in a Standard game */
  886. X        if(inprogress == True && gametype != Standard)
  887. X            reason = "Sorry, the current game is not Standard.";
  888. X        else if(blitzwhen == Enforced && workhourstype() == Blitz)
  889. X            reason = "Sorry, only Blitz games may be played now.";
  890. X        break;
  891. X    case Fblitz:    /* only wants to play in a Blitz game */
  892. X        if(inprogress == True && gametype != Blitz)
  893. X            reason = "Sorry, the current game is not Blitz.";
  894. X        else if(blitzwhen == Noblitz)
  895. X            reason = "Sorry, this server doesn't play Blitz.";
  896. X        break;
  897. X    }
  898. X
  899. X    /*
  900. X    **    Tell them why we're saying goodbye and hang up.
  901. X    */
  902. X    if(reason != 0) {
  903. X        (void) simp(c, M_DOWN, reason);
  904. X        oldplayer(c);
  905. X        return False;
  906. X    }
  907. X
  908. X    return True;
  909. X}
  910. X
  911. X/*
  912. X**    oldplayer: remove a player from the game
  913. X*/
  914. Xoldplayer(c)
  915. Xregister int    c;
  916. X{
  917. X    /*
  918. X    **    Close the socket if one is open.
  919. X    */
  920. X    if(plr[c].p_fd != -1) {
  921. X        (void) close(plr[c].p_fd);
  922. X        plr[c].p_fd = -1, plr[c].p_timeouts = 0;
  923. X    }
  924. X
  925. X    /*
  926. X    **    Change the player's status.
  927. X    */
  928. X    switch(plr[c].p_stat) {
  929. X    case Inactive:
  930. X        break;
  931. X
  932. X    case Active:
  933. X        /*
  934. X        **    If game in progress and there are other humans playing and this
  935. X        **    player is on board, then there's a good chance that a Computer
  936. X        **    proxy will be assigned to take over.  The proxy keeps the original
  937. X        **    name so that other players are not made aware.  The id is also
  938. X        **    left unchanged so that a single player can't flood the game with
  939. X        **    proxies.  We do this substitution, more rarely, when a player
  940. X        **    is not yet on board, to penalize players that repeatedly start
  941. X        **    and quit games looking for an initial big advantage.
  942. X        */
  943. X        if(inprogress == True && active > 1) {
  944. X            if(    isproxy(c) == True            /* already a temporary proxy */
  945. X            ||    (plr[c].p_onboard == True && randint(5) > 2)    /* 3/5ths */
  946. X            ||    (plr[c].p_onboard == False && randint(5) == 1)    /* 1/5th */
  947. X            ) {
  948. X                pickproxy(c);    /* set up proxy personality */
  949. X                plr[c].p_stat = Computer, --active;    /* permanent proxy */
  950. X                return;            /* all done */
  951. X            }
  952. X        }
  953. X
  954. X        /*
  955. X        **    If the player wasn't replaced, then we remove.
  956. X        **    If a game is in progress adjust player's history.
  957. X        */
  958. X        if(inprogress == True)
  959. X            histpoints(c);
  960. X        plr[c].p_stat = Inactive, --active;
  961. X        if(active < 0) {
  962. X            syslog(LOG_DEBUG,
  963. X                "oldplayer: active=%d (shouldn't be negative)", active);
  964. X            active = 0;
  965. X        }
  966. X        if(active == 0)
  967. X            inprogress = False;    /* XXX: update Computer points history? */
  968. X        annfarewell(c);
  969. X        break;
  970. X
  971. X    case Waiting:
  972. X        /*
  973. X        **    If observers are showing in the roster, and this is not a
  974. X        **    Waiting Computer, then announce farewell.
  975. X        */
  976. X        plr[c].p_stat = Inactive, --waiting;
  977. X        if(waiting < 0) {
  978. X            syslog(LOG_DEBUG,
  979. X                "oldplayer: waiting=%d (shouldn't be negative)", waiting);
  980. X            waiting = 0;
  981. X        }
  982. X        if(clearobs == True && plr[c].p_computer == 0)
  983. X            annfarewell(c);
  984. X        break;
  985. X
  986. X    case Watching:
  987. X        plr[c].p_stat = Inactive, --watching;
  988. X        if(watching < 0) {
  989. X            syslog(LOG_DEBUG,
  990. X                "oldplayer: watching=%d (shouldn't be negative)", watching);
  991. X            watching = 0;
  992. X        }
  993. X        if(clearobs == True)        /* observers are showing in roster */
  994. X            annfarewell(c);            /* clear this one */
  995. X        break;
  996. X
  997. X    case Spider:
  998. X        plr[c].p_stat = Inactive, --spiders;
  999. X        if(spiders < 0) {
  1000. X            syslog(LOG_DEBUG,
  1001. X                "oldplayer: spiders=%d (shouldn't be negative)", spiders);
  1002. X            spiders = 0;
  1003. X        }
  1004. X        break;
  1005. X
  1006. X    case Computer:
  1007. X        plr[c].p_stat = Inactive;
  1008. X        if(inprogress == True)
  1009. X            histpoints(c);
  1010. X        annfarewell(c);
  1011. X        break;
  1012. X    }
  1013. X
  1014. X    zeroplayer(c);
  1015. X    (void) observers(-1);
  1016. X}
  1017. X
  1018. X/*
  1019. X**    zeroplayer: initialize a player
  1020. X*/
  1021. Xzeroplayer(c)
  1022. Xint        c;
  1023. X{
  1024. X    register player       *p = &plr[c];
  1025. X
  1026. X    p->p_stat = Inactive, p->p_computer = 0;
  1027. X    p->p_score = p->p_squander = 0, p->p_onboard = False;
  1028. X    p->p_fd = -1, p->p_timeouts = 0;
  1029. X    p->p_pref = Nopref, p->p_mood = 0;
  1030. X    p->p_name[0] = p->p_id[0] = '\0';
  1031. X}
  1032. X
  1033. X/*
  1034. X**    fillroster: add computers to fill in roster
  1035. X*/
  1036. Xfillroster(pplr, pcomp)
  1037. Xint       *pplr, *pcomp;
  1038. X{
  1039. X    register int    c, nplr, ncomp;
  1040. X
  1041. X    /*
  1042. X    **    Count all players.  We assume that all types of humans will
  1043. X    **    play in the next game.  We add Computers in Waiting mode
  1044. X    **    so we have to count them specially.
  1045. X    */
  1046. X    ncomp = nplr = 0;
  1047. X    for(c = 0;c < PLAYERS;++c) {
  1048. X        switch(plr[c].p_stat) {
  1049. X        case Inactive:
  1050. X            break;
  1051. X        case Waiting:
  1052. X            if(plr[c].p_computer != 0)
  1053. X                ++ncomp;
  1054. X            ++nplr;
  1055. X            break;
  1056. X        case Computer:
  1057. X            ++ncomp, ++nplr;
  1058. X            break;
  1059. X        default:
  1060. X            ++nplr;
  1061. X            break;
  1062. X        }
  1063. X    }
  1064. X    
  1065. X    /*
  1066. X    **    Add enough Computer players to make at least MINCOMP.
  1067. X    **    Add enough Computer players to make MINPLR players total.
  1068. X    **    Add Computer players randomly with probability that
  1069. X    **        decreases with increasing number of players.
  1070. X    */
  1071. X    while(ncomp < MINCOMP || nplr < MINPLR || randint(nplr+2) == 1) {
  1072. X        for(c = 0;c < PLAYERS;++c) if(plr[c].p_stat == Inactive) {
  1073. X            pickcomputer(c);                    /* sets status to Computer */
  1074. X            plr[c].p_stat = Waiting, ++waiting;    /* no Waiting Computer mode */
  1075. X            plr[c].p_fd = -1, plr[c].p_timeouts = 0;
  1076. X            plr[c].p_score = plr[c].p_squander = 0, plr[c].p_onboard = False;
  1077. X            ++ncomp, ++nplr;
  1078. X            break;
  1079. X        }
  1080. X    }
  1081. X
  1082. X    *pplr = nplr, *pcomp = ncomp;
  1083. X}
  1084. X
  1085. X/*
  1086. X**    workhourstype: calculate default game type for this time of day
  1087. X**        Working hours: 8:00 - 11:59 AM, 12:45 - 4:45 PM Mon - Fri.
  1088. XXXX:    Sorry, there's no accounting for holidays.
  1089. X*/
  1090. Xwinpref
  1091. Xworkhourstype()
  1092. X{
  1093. X    time_t        now;
  1094. X    struct tm  *tm;
  1095. X
  1096. X    if(blitzwhen != Workhours && blitzwhen != Enforced)
  1097. X        return Standard;
  1098. X
  1099. X    (void) time(&now);
  1100. X    tm = localtime(&now);
  1101. X
  1102. X    if(tm->tm_wday >= 1 && tm->tm_wday <= 5) {                /* Mon thru Fri? */
  1103. X        if(tm->tm_hour >= 8 && tm->tm_hour <= 16) {            /* 8:00 to 4:59 */
  1104. X            if(tm->tm_hour == 12 && tm->tm_min <= 45)        /* Noon to 12:45 */
  1105. X                ;                                            /* lunch */
  1106. X            else if(tm->tm_hour == 16 && tm->tm_min > 45)    /* 4:45 to 4:59 */
  1107. X                ;                                            /* end of day */
  1108. X            else return Blitz;                                /* work hours */
  1109. X        }
  1110. X    }
  1111. X
  1112. X    return Standard;
  1113. X}
  1114. X
  1115. X/*
  1116. X**    votegametype: try to find a consensus amongst the players
  1117. X*/
  1118. Xwinpref
  1119. Xvotegametype()
  1120. X{
  1121. X    winpref            pref;
  1122. X    register int    c;
  1123. X
  1124. X    pref = Nopref;
  1125. X    for(c = 0;c < PLAYERS;++c) {
  1126. X        if(plr[c].p_stat == Inactive || plr[c].p_pref == Nopref)
  1127. X            continue;
  1128. X        switch(pref) {
  1129. X        case Standard:
  1130. X            if(plr[c].p_pref != Standard && plr[c].p_pref != Fstand)
  1131. X                return Nopref;
  1132. X            break;
  1133. X        case Blitz:
  1134. X            if(plr[c].p_pref != Blitz && plr[c].p_pref != Fblitz)
  1135. X                return Nopref;
  1136. X            break;
  1137. X        default:
  1138. X            switch(plr[c].p_pref) {
  1139. X            case Standard: case Fstand: pref = Standard; break;
  1140. X            case Blitz:    case Fblitz: pref = Blitz; break;
  1141. X            }
  1142. X            break;
  1143. X        }
  1144. X    }
  1145. X
  1146. X    return pref;
  1147. X}
  1148. X
  1149. X/*
  1150. X**    pickgametype: select gametype, winscore, and jokermode
  1151. X*/
  1152. Xpickgametype()
  1153. X{
  1154. X    winpref        temp;
  1155. X
  1156. X    switch(blitzwhen) {
  1157. X    case Noblitz:
  1158. X        gametype = Standard;
  1159. X        break;
  1160. X    case Onrequest:
  1161. X        if((gametype = votegametype()) == Nopref)
  1162. X            gametype = Standard;
  1163. X        break;
  1164. X    case Workhours:
  1165. X        if((gametype = votegametype()) == Nopref)
  1166. X            gametype = workhourstype();
  1167. X        break;
  1168. X    case Enforced:
  1169. X        if((gametype = workhourstype()) != Blitz)
  1170. X            if((temp = votegametype()) != Nopref)
  1171. X                gametype = temp;
  1172. X        break;
  1173. X    }
  1174. X
  1175. X    winscore = (gametype == Blitz) ? BLITZSCORE : WINSCORE;
  1176. X
  1177. X    if(enajokers == False)
  1178. X        jokermode = False;
  1179. X    else
  1180. X        jokermode = randint(6) == 6 ? True : False;
  1181. X}
  1182. X
  1183. X/*
  1184. X**    getequiv: determine equivalent hosts.
  1185. X*/
  1186. Xgetequiv()
  1187. X{
  1188. X    register FILE  *fp;
  1189. X    register int    n;
  1190. X    char            buf[512];
  1191. X
  1192. X    /*
  1193. X    **    Open the hosts.equiv file and count the entries.
  1194. X    **    Allocate memory for them and this host.
  1195. X    */
  1196. X    neq = 1;
  1197. X    if((fp = fopen("/etc/hosts.equiv", "r")) != 0) {
  1198. X        while(fgets(buf, sizeof buf, fp) != 0)
  1199. X            ++neq;
  1200. X        clearerr(fp);
  1201. X        rewind(fp);
  1202. X    }
  1203. X    if((equiv = (char **)malloc(neq * sizeof *equiv)) == 0) {
  1204. X        syslog(LOG_ERR, "malloc: no memory for equivalent hosts");
  1205. X        exit(1);
  1206. X    }
  1207. X
  1208. X    /*
  1209. X    **    This host is first in the list.
  1210. X    */
  1211. X    (void) gethostname(buf, sizeof buf);
  1212. X    if((equiv[0] = malloc((unsigned)(strlen(buf) + 1))) == 0) {
  1213. X        syslog(LOG_ERR, "malloc: no memory for this host");
  1214. X        exit(1);
  1215. X    }
  1216. X    strcpy(equiv[0], buf);
  1217. X
  1218. X    /*
  1219. X    **    Add the hosts from hosts.equiv.
  1220. X    */
  1221. X    if(fp != 0) {
  1222. X        for(n = 1;n < neq;++n) {
  1223. X            if(gets(buf) == 0) {
  1224. X                neq = n;
  1225. X                break;
  1226. X            }
  1227. X            if((equiv[n] = malloc((unsigned)(strlen(buf) + 1))) == 0) {
  1228. X                syslog(LOG_ERR, "malloc: no memory for an equivalent host");
  1229. X                exit(1);
  1230. X            }
  1231. X            strcpy(equiv[n], buf);
  1232. X        }
  1233. X        (void) fclose(fp);
  1234. X    }
  1235. X}
  1236. X
  1237. X/*
  1238. X**    equivalence: strip "@thishost" or "@equivhost" from an id
  1239. X*/
  1240. Xequivalence(id)
  1241. Xchar   *id;
  1242. X{
  1243. X    register char  *at;
  1244. X    register int    n;
  1245. X
  1246. X    if((at = index(id, '@')) == 0)
  1247. X        return;
  1248. X    if(neq == 0)
  1249. X        getequiv();
  1250. X    for(n = 0;n < neq;++n) {
  1251. X        if(strcmp(at+1, equiv[n]) == 0) {
  1252. X            *at = '\0';
  1253. X            return;
  1254. X        }
  1255. X    }
  1256. X}
  1257. X
  1258. X/*
  1259. X**    idinuse: return True if id is in use by a human
  1260. X**        or is in use by more than one Computer player
  1261. X**    side effect: converts user@thishost or user@equivhost to just user
  1262. X*/
  1263. Xboolean
  1264. Xidinuse(c, id)
  1265. Xint        c;
  1266. Xchar   *id;
  1267. X{
  1268. X    register int    cc;
  1269. X    int                humanuse;
  1270. X    int                computeruse;
  1271. X
  1272. X    equivalence(id);    /* strip @equivhost */
  1273. X
  1274. X    humanuse = computeruse = 0;
  1275. X    for(cc = 0;cc < PLAYERS;++cc) if(cc != c) {
  1276. X        if(strncmp(plr[cc].p_id, id, IDLEN-1) == 0) {
  1277. X            switch(plr[cc].p_stat) {
  1278. X            case Active:
  1279. X            case Waiting:
  1280. X            case Watching:
  1281. X            case Spider:
  1282. X                ++humanuse;
  1283. X                break;
  1284. X            case Computer:
  1285. X                ++computeruse;
  1286. X                break;
  1287. X            default:
  1288. X                break;
  1289. X            }
  1290. X        }
  1291. X    }
  1292. X
  1293. X    if(humanuse > 0 || computeruse > 1)
  1294. X        return True;
  1295. X
  1296. X    return False;
  1297. X}
  1298. X
  1299. X/*
  1300. X**    getplayername: get the player's name and check it
  1301. X*/
  1302. Xgetplayername(c)
  1303. Xint            c;
  1304. X{
  1305. X    register char  *aka;
  1306. X    char            msgbuf[MESGLEN];
  1307. X
  1308. X    /*
  1309. X    **    Prompt for and read name.
  1310. X    */
  1311. X    sprintf(msgbuf,
  1312. X        "%d Hello, I'm the Cube daemon.  Who are you?\r\n", M_HELO);
  1313. X    if(dialogue(c, msgbuf, sizeof msgbuf) < 0) {
  1314. X        if(plr[c].p_stat != Inactive) {    /* in case of timeout */
  1315. X            (void) simp(c, M_DOWN, "Server closing connection.");
  1316. X            oldplayer(c);
  1317. X        }
  1318. X        return -1;
  1319. X    }
  1320. X
  1321. X    /*
  1322. X    **    Skip past any leading spaces or tabs in the response.
  1323. X    */
  1324. X    for(aka = msgbuf;*aka == ' ' || *aka == '\t';++aka)
  1325. X        ;
  1326. X
  1327. X    /*
  1328. X    **    If a non-null name was supplied, check that it is not in use.
  1329. X    **    If not, save the name in the list and we're done.
  1330. X    */
  1331. X    if(*aka != '\0') {
  1332. X        strncpy(plr[c].p_name, aka, NAMELEN);
  1333. X        plr[c].p_name[NAMELEN-1] = '\0';
  1334. X        if(nameinuse(c, plr[c].p_name) == False) {
  1335. X            savename(plr[c].p_name);
  1336. X            return 0;
  1337. X        }
  1338. X        (void) simp(c, M_ARGE, "Your chosen moniker is already in use.");
  1339. X    }
  1340. X
  1341. X    /*
  1342. X    **    We were supplied with a null name, or one already in use.
  1343. X    **    Pick one from the monikers list rather than dumping the player.
  1344. X    */
  1345. X    while(nameinuse(c, (aka = moniker())) == True)
  1346. X        ;
  1347. X    strncpy(plr[c].p_name, aka, NAMELEN);
  1348. X    plr[c].p_name[NAMELEN-1] = '\0';
  1349. X
  1350. X    return 0;
  1351. X}
  1352. X
  1353. X/*
  1354. X**    getplayerid: get player's unique id (user@host)
  1355. X**        Allow setting of winscore preference.
  1356. X*/
  1357. Xgetplayerid(c)
  1358. Xint            c;
  1359. X{
  1360. X    register char  *s, *id;
  1361. X    char            msgbuf[MESGLEN];
  1362. X
  1363. X    /*
  1364. X    **    Prompt for and read the id.
  1365. X    */
  1366. X    sprintf(msgbuf, "%d Please supply a unique id.\r\n", M_RQID);
  1367. X    if(dialogue(c, msgbuf, sizeof msgbuf) < 0) {
  1368. X        if(plr[c].p_stat != Inactive) {    /* in case of timeout */
  1369. X            sprintf(msgbuf, "%d No response taken to mean `No.'\r\n", M_ARGE);
  1370. X            goto iderror;
  1371. X        }
  1372. X        return -1;
  1373. X    }
  1374. X
  1375. X    /*
  1376. X    **    A null id is not valid.
  1377. X    */
  1378. X    if(msgbuf[0] == '\0') {
  1379. X        sprintf(msgbuf, "%d Sorry, that's not unique.\r\n", M_ARGE);
  1380. X        goto iderror;
  1381. X    }
  1382. X
  1383. X    /*
  1384. X    **    Check for winscore preference.  Ignore bogus values.
  1385. X    */
  1386. X    if(msgbuf[1] != ';') {
  1387. X        plr[c].p_pref = Nopref;
  1388. X        id = &msgbuf[0];
  1389. X    } else {
  1390. X        switch(msgbuf[0]) {
  1391. X        case 's': plr[c].p_pref = Standard; break;
  1392. X        case 'S': plr[c].p_pref = Fstand; break;
  1393. X        case 'b': plr[c].p_pref = Blitz; break;
  1394. X        case 'B': plr[c].p_pref = Fblitz; break;
  1395. X        default:  plr[c].p_pref = Nopref; break;
  1396. X        }
  1397. X        id = &msgbuf[2];
  1398. X    }
  1399. X
  1400. X    /*
  1401. X    **    Id's are alphanumerics plus a few other characters.
  1402. X    */
  1403. X    for(s = id;*s != '\0';++s) {
  1404. X        if(isalnum(*s) || index("@._-+=", *s) != 0)
  1405. X            continue;
  1406. X        sprintf(msgbuf, "%d `%c' is not allowed in an id.\r\n", M_ARGE, *s);
  1407. X        goto iderror;
  1408. X    }
  1409. X
  1410. X    /*
  1411. X    **    Check to see that the id is not already in use.
  1412. X    */
  1413. X    strncpy(plr[c].p_id, id, IDLEN);
  1414. X    plr[c].p_id[IDLEN-1] = '\0';
  1415. X    if(idinuse(c, plr[c].p_id) == True) {
  1416. X        sprintf(msgbuf, "%d You're already playing!\r\n", M_ARGE);
  1417. X        goto iderror;
  1418. X    }
  1419. X
  1420. X    /*
  1421. X    **    Success.
  1422. X    */
  1423. X    return 0;
  1424. X
  1425. X    /*
  1426. X    **    Common error handling code.
  1427. X    */
  1428. Xiderror:
  1429. X    if(message(c, msgbuf) < 0)
  1430. X        return -1;
  1431. X    (void) simp(c, M_DOWN, "Server closing connection.");
  1432. X    oldplayer(c);
  1433. X    return -1;
  1434. X}
  1435. X
  1436. X/*
  1437. X**    getplayerintent: get the player's intent
  1438. X**        If a game is inprogress, the question is watch or play?
  1439. X**        Otherwise, the question is wait or play?
  1440. X**    Assumes that player status is already set to Watching.
  1441. X*/
  1442. Xgetplayerintent(c)
  1443. Xint            c;
  1444. X{
  1445. X    history       *ph;
  1446. X    char        msgbuf[MESGLEN];
  1447. X
  1448. X    /*
  1449. X    **    Prompt for and read intent.
  1450. X    */
  1451. X    sprintf(msgbuf, "%d Do you want to %s or play? [wp]\r\n",
  1452. X        inprogress == True ? M_WORP : M_SORP,
  1453. X        inprogress == True ? "watch" : "wait");
  1454. X    if(dialogue(c, msgbuf, sizeof msgbuf) < 0) {
  1455. X        if(plr[c].p_stat != Inactive) {    /* in case of timeout */
  1456. X            (void) simp(c, M_DOWN, "Server closing connection.");
  1457. X            oldplayer(c);
  1458. X        }
  1459. X        return -1;
  1460. X    }
  1461. X    
  1462. X    /*
  1463. X    **    Check it for validity.
  1464. X    */
  1465. X    switch(msgbuf[0]) {
  1466. X    case '\0':                /* default */
  1467. X    case 'p': case 'P':        /* Play */
  1468. X        plr[c].p_stat = Waiting, ++waiting, --watching;
  1469. X        return 0;
  1470. X    case 'w': case 'W':        /* Watch or Wait */
  1471. X        if(inprogress == False) {
  1472. X            plr[c].p_stat = Spider, ++spiders, --watching;
  1473. X            return 0;
  1474. X        }
  1475. X        if(active <= 0 && waiting <= 0) {
  1476. X            if(simp(c, M_ARGE, "Sorry, no game in progress.") < 0)
  1477. X                return -1;
  1478. X            break;
  1479. X        }
  1480. X        /*
  1481. X        **    We let new players watch, but we don't let ancient ones do so.
  1482. X        **    It seems that some people would rather watch than play, but
  1483. X        **    that's no fun for the regular players.
  1484. X        */
  1485. X        if((ph = histbyname(plr[c].p_id)) != 0) {
  1486. X            if(ph->h_lastgame > 0 && gamenum - ph->h_lastgame >= ANCIENT) {
  1487. X                syslog(LOG_NOTICE,
  1488. X                    "getplayerintent: excluded ancient voyeur %s", plr[c].p_id);
  1489. X                if(simp(c, M_ARGE, "Sorry, you're too ancient to watch!") < 0)
  1490. X                    return -1;
  1491. X                break;
  1492. X            }
  1493. X        }
  1494. X        return 0;
  1495. X    default:
  1496. X        if(simp(c, M_ARGE, "Sorry, but that's nonsense.") < 0)
  1497. X            return -1;
  1498. X        break;
  1499. X    }
  1500. X
  1501. X    (void) simp(c, M_DOWN, "Server closing connection.");
  1502. X    oldplayer(c);
  1503. X    return -1;
  1504. X}
  1505. X
  1506. X/*
  1507. X**    fixroster: do any necessary roster adjustment
  1508. X**        If add is true, we can add Waiting players to the front of
  1509. X**        the roster.  If false, we will move them to the end.
  1510. X**        Collapses out Inactive slots.
  1511. X*/
  1512. Xfixroster(add)
  1513. Xboolean        add;
  1514. X{
  1515. X    int        c, cc, low, squ;
  1516. X    player    tmp[PLAYERS];
  1517. X
  1518. X    /*
  1519. X    **    No reason to do this except during a game.
  1520. X    */
  1521. X    if(inprogress == False)
  1522. X        return;
  1523. X
  1524. X    /*
  1525. X    **    If there are no Waiting players, or we're not allowed to add them,
  1526. X    **    check for "empty" slots in the roster.  We define empty here as
  1527. X    **    Inactive, Watching, or Waiting, since to Active players, slots held
  1528. X    **    by Watching or Waiting players appear empty.
  1529. X    */
  1530. X    if(waiting == 0 || add == False) {
  1531. X        for(c = 0;c < PLAYERS;++c)        /* find first empty slot */
  1532. X            if(plr[c].p_stat != Computer && plr[c].p_stat != Active)
  1533. X                break;
  1534. X        for(++c;c < PLAYERS;++c)        /* look for a non-empty slot */
  1535. X            if(plr[c].p_stat == Computer || plr[c].p_stat == Active)
  1536. X                break;
  1537. X        if(c >= PLAYERS)                /* didn't find any */
  1538. X            return;
  1539. X    }
  1540. X
  1541. X    /*
  1542. X    **    If we are allowed to add Waiting players, put them first,
  1543. X    **    otherwise put them last with the Watching players.  In either
  1544. X    **    case, this algorithm removes empty slots.
  1545. X    */
  1546. X    cc = 0;
  1547. X    if(add == True) {
  1548. X        for(c = 0;c < PLAYERS;++c)    /* first pass, Waiting players */
  1549. X            if(plr[c].p_stat == Waiting)
  1550. X                tmp[cc++] = plr[c];
  1551. X    }
  1552. X    for(c = 0;c < PLAYERS;++c)        /* second pass, Active players */
  1553. X        if(plr[c].p_stat == Computer || plr[c].p_stat == Active)
  1554. X            tmp[cc++] = plr[c];
  1555. X    for(c = 0;c < PLAYERS;++c)        /* third pass, Watching players */
  1556. X        if(    plr[c].p_stat == Watching
  1557. X        || plr[c].p_stat == Spider
  1558. X        ||    (add == False && plr[c].p_stat == Waiting))
  1559. X            tmp[cc++] = plr[c];
  1560. X    if(cc == 0) {
  1561. X        syslog(LOG_DEBUG, "fixroster: no players!");
  1562. X        active = waiting = watching = 0;
  1563. X        return;
  1564. X    }
  1565. X
  1566. X    /*
  1567. X    **    Calculate a suitable low score for any new players.
  1568. X    */
  1569. X    if(waiting == 0 || add == False || (low = lowscore(&squ) - ONBOARD) < 0)
  1570. X        low = 0;
  1571. X
  1572. X    /*
  1573. X    **    Copy the reordered roster into the real one while at the
  1574. X    **    same time adding waiting players (if allowed).
  1575. X    */
  1576. X    for(c = 0;c < cc;++c) {
  1577. X        plr[c] = tmp[c];
  1578. X        if(add == True && plr[c].p_stat == Waiting) {
  1579. X            plr[c].p_score = low;
  1580. X            plr[c].p_squander = squ;
  1581. X            plr[c].p_onboard = False;
  1582. X            if(plr[c].p_computer != 0)
  1583. X                plr[c].p_stat = Computer, --waiting;
  1584. X            else {
  1585. X                plr[c].p_stat = Active, --waiting, ++active;
  1586. X                (void) tellstatus(c);    /* XXX */
  1587. X            }
  1588. X        }
  1589. X    }
  1590. X    for(;c < PLAYERS;++c)
  1591. X        zeroplayer(c);
  1592. X    if(waiting != 0) {
  1593. X        syslog(LOG_DEBUG, "fixroster: waiting=%d (should be zero)", waiting);
  1594. X        waiting = 0;
  1595. X    }
  1596. X
  1597. X    /*
  1598. X    **    Broadcast the new roster to all players.
  1599. X    */
  1600. X    roster(-1, False);
  1601. X}
  1602. END_OF_FILE
  1603. if test 18633 -ne `wc -c <'cubeserv2.c'`; then
  1604.     echo shar: \"'cubeserv2.c'\" unpacked with wrong size!
  1605. fi
  1606. # end of 'cubeserv2.c'
  1607. fi
  1608. if test -f 'history.c' -a "${1}" != "-c" ; then 
  1609.   echo shar: Will not clobber existing file \"'history.c'\"
  1610. else
  1611. echo shar: Extracting \"'history.c'\" \(17309 characters\)
  1612. sed "s/^X//" >'history.c' <<'END_OF_FILE'
  1613. X/*    vi:set sw=4 ts=4: */
  1614. X#ifndef    lint
  1615. Xstatic char    sccsid[] = "@(#)history.c 5.1 (G.M. Paris) 89/01/22";
  1616. X#endif    lint
  1617. X
  1618. X/*
  1619. X**
  1620. X**    cubes 5.1  Copyright 1989 Gregory M. Paris
  1621. X**        Permission granted to redistribute on a no charge basis.
  1622. X**        All other rights are reserved.
  1623. X**
  1624. X*/
  1625. X
  1626. X#include    <stdio.h>
  1627. X#include    <syslog.h>
  1628. X#include    <signal.h>
  1629. X#include    <sys/file.h>
  1630. X#include    <sys/types.h>
  1631. X#include    <sys/stat.h>
  1632. X#include    <strings.h>
  1633. X#include    "cubes.h"
  1634. X
  1635. X#define        MVGAMES        25            /* number of games for moving avg */
  1636. X
  1637. Xhistory               *hist;            /* history array */
  1638. Xunsigned            nhist    = 0;    /* entries in history array */
  1639. Xlong                gamenum;        /* current game number */
  1640. Xptstype                ptsmode    = Combined;    /* history points used in ranking */
  1641. X
  1642. Xextern int            winscore;
  1643. Xextern int            turnnum;
  1644. Xextern player        plr[];
  1645. Xextern int            plrcmp();
  1646. Xextern long            histwgt();
  1647. Xextern long            histwgtplr();
  1648. Xstatic history       *histadd();
  1649. Xextern history       *histbyname();
  1650. Xextern computer        comptbl[];
  1651. Xextern computer       *compbyname();
  1652. Xextern boolean        iscomp();
  1653. Xextern boolean        iskamelion();
  1654. X
  1655. Xextern char           *fgets();
  1656. Xextern char           *malloc();
  1657. Xextern char           *realloc();
  1658. X
  1659. X/*
  1660. X**    histread: read history file
  1661. X**        Any format conversions must be handled by a separate utility.
  1662. X*/
  1663. Xhistread(histfile)
  1664. Xchar   *histfile;
  1665. X{
  1666. X    register history   *ph;
  1667. X    register int        n;
  1668. X    register FILE       *fp;
  1669. X    char                line[BUFSIZ];
  1670. X    char                dupid[IDLEN];
  1671. X    long                duplastgame;
  1672. X    int                    omask;
  1673. X
  1674. X    omask = sigsetmask(~0);
  1675. X
  1676. X    if(hist != 0) {
  1677. X        free((char *)hist);
  1678. X        hist = 0, nhist = 0;
  1679. X    }
  1680. X
  1681. X    /*
  1682. X    **    Open the file for reading.  The file must exist.
  1683. X    */
  1684. X    if((fp = fopen(histfile, "r")) == 0) {
  1685. X        syslog(LOG_ERR, "histread: fopen(%s,r): %m", histfile);
  1686. X        (void) sigsetmask(omask);
  1687. X        return -1;
  1688. X    }
  1689. X    /*
  1690. X    **    Count the lines in the file.  If none, we're all done.
  1691. X    */
  1692. X    for(n = 0;fgets(line, sizeof line, fp) != 0;++n)
  1693. X        ;
  1694. X    if(n == 0) {
  1695. X        (void) fclose(fp);
  1696. X        (void) sigsetmask(omask);
  1697. X        return 0;
  1698. X    }
  1699. X
  1700. X    /*
  1701. X    **    Rewind the file, allocate space, and read in the entries.
  1702. X    **    There are now two lines per player.
  1703. X    */
  1704. X    clearerr(fp);
  1705. X    rewind(fp);
  1706. X    nhist = n / 2;
  1707. X    if((hist = (history *)malloc(nhist * sizeof *hist)) == 0) {
  1708. X        syslog(LOG_ERR, "histread: malloc: can't allocate space for history");
  1709. X        (void) fclose(fp);
  1710. X        (void) sigsetmask(omask);
  1711. X        return -1;
  1712. X    }
  1713. X
  1714. X    for(n = 0, ph = hist;;++n, ++ph) {
  1715. X        if(fgets(line, sizeof line, fp) == 0)
  1716. X            break;
  1717. X        if(sscanf(line, "L %ld %ld %ld %ld %ld%*[ ]%[^\n]",
  1718. X            &ph->h_games, &ph->h_wins, &ph->h_points, &ph->h_avgturn,
  1719. X            &ph->h_lastgame, ph->h_id) != 6) {
  1720. X            syslog(LOG_DEBUG, "histread: got bad lifetime in %s", histfile);
  1721. X            continue;
  1722. X        }
  1723. X        if(fgets(line, sizeof line, fp) == 0) {
  1724. X            syslog(LOG_DEBUG, "histread: missing last moving in %s", histfile);
  1725. X            break;
  1726. X        }
  1727. X        if(sscanf(line, "M %ld %ld %ld %ld %ld%*[ ]%[^\n]",
  1728. X            &ph->h_mvgames, &ph->h_mvwins, &ph->h_mvpoints, &ph->h_mvavgturn,
  1729. X            &duplastgame, dupid) != 6) {
  1730. X            syslog(LOG_DEBUG, "histread: got bad moving in %s", histfile);
  1731. X            continue;
  1732. X        }
  1733. X        if(strcmp(ph->h_id, dupid) != 0) {
  1734. X            syslog(LOG_DEBUG, "histread: name mismatch %ls vs %ls in %s",
  1735. X                ph->h_id, dupid, histfile);
  1736. X            continue;
  1737. X        }
  1738. X        if(ph->h_lastgame != duplastgame) {
  1739. X            syslog(LOG_DEBUG,
  1740. X                "histread: lastgame mismatch %ld vs %ld for %s in %s",
  1741. X                ph->h_lastgame, duplastgame, ph->h_id, histfile);
  1742. X            continue;
  1743. X        }
  1744. X        ph->h_computer = compbyname(ph->h_id);
  1745. X    }
  1746. X
  1747. X    (void) fclose(fp);
  1748. X
  1749. X    (void) sigsetmask(omask);
  1750. X    histsort();
  1751. X
  1752. X    return 0;
  1753. X}
  1754. X
  1755. X/*
  1756. X**    histprune: remove ancient records from history
  1757. X**        assumes gamenum has been set
  1758. X*/
  1759. Xhistprune(gpcredit)
  1760. Xint        gpcredit;
  1761. X{
  1762. X    register int        h;
  1763. X    register history   *pt, *pf;
  1764. X    register long        span;
  1765. X    unsigned            nnhist;
  1766. X    int                    omask;
  1767. X
  1768. X    omask = sigsetmask(~0);
  1769. X
  1770. X    /*
  1771. X    **    Look for ancient records and delete them.
  1772. X    */
  1773. X    pt = pf = hist;
  1774. X    for(h = 0;h < nhist;++h, ++pf) {
  1775. X        span = gamenum - pf->h_lastgame;
  1776. X        if(gpcredit != 0)
  1777. X            span -= gpcredit * pf->h_games;
  1778. X        if(span >= ANCIENT)
  1779. X            continue;
  1780. X        if(pt != pf)
  1781. X            *pt = *pf;
  1782. X        ++pt;
  1783. X    }
  1784. X
  1785. X    /*
  1786. X    **    Shrink memory used by history to account for pruned entries.
  1787. X    */
  1788. X    nnhist = (unsigned)(pt - hist);
  1789. X    if(nnhist != nhist) {
  1790. X        if((hist =
  1791. X            (history *)realloc((char *)hist, nnhist * sizeof *hist)) == 0) {
  1792. X            syslog(LOG_WARNING, "histprune: realloc failed");
  1793. X            nnhist = 0;
  1794. X        }
  1795. X        nhist = nnhist;
  1796. X    }
  1797. X
  1798. X    (void) sigsetmask(omask);
  1799. X}
  1800. X
  1801. X/*
  1802. X**    histcmp: history comparison function for history file ordering
  1803. X*/
  1804. Xhistcmp(h1, h2)
  1805. Xhistory       *h1, *h2;
  1806. X{
  1807. X    return (int)(h2->h_weight - h1->h_weight);
  1808. X}
  1809. X
  1810. X/*
  1811. X**    histsort: sort the history array
  1812. X*/
  1813. Xhistsort()
  1814. X{
  1815. X    register int    n;
  1816. X    int                omask;
  1817. X
  1818. X    if(nhist != 0 && hist != 0) {
  1819. X        for(n = 0;n < nhist;++n)
  1820. X            (void) histwgt(n);
  1821. X        omask = sigsetmask(~0);
  1822. X        qsort((char *)hist, (int)nhist, sizeof *hist, histcmp);
  1823. X        (void) sigsetmask(omask);
  1824. X        for(n = 0;n < nhist;++n)
  1825. X            (hist+n)->h_rank = n + 1;
  1826. X    }
  1827. X}
  1828. X
  1829. X/*
  1830. X**    histwrite: write the history file, pruning ancient records
  1831. X*/
  1832. Xhistwrite(histfile)
  1833. Xchar   *histfile;
  1834. X{
  1835. X    register history   *ph;
  1836. X    register FILE       *fp;
  1837. X    register int        n;
  1838. X    int                    omask;
  1839. X
  1840. X    omask = sigsetmask(~0);        /* block all signals */
  1841. X
  1842. X    (void) umask(0644);
  1843. X    if((fp = fopen(histfile, "w")) == 0) {
  1844. X        syslog(LOG_ERR, "histwrite: fopen(%s,w): %m", histfile);
  1845. X        (void) sigsetmask(omask);
  1846. X        return -1;
  1847. X    }
  1848. X
  1849. X    if(nhist != 0) {
  1850. X        histprune(WRITECREDIT);
  1851. X        histsort();
  1852. X        for(n = 0, ph = hist;n < nhist;++n, ++ph) {
  1853. X            fprintf(fp, "L %5ld %5ld %10ld %10ld %6ld %s\n",
  1854. X                ph->h_games, ph->h_wins, ph->h_points, ph->h_avgturn,
  1855. X                ph->h_lastgame, ph->h_id);
  1856. X            fprintf(fp, "M %5ld %5ld %10ld %10ld %6ld %s\n",
  1857. X                ph->h_mvgames, ph->h_mvwins, ph->h_mvpoints, ph->h_mvavgturn,
  1858. X                ph->h_lastgame, ph->h_id);
  1859. X        }
  1860. X    }
  1861. X
  1862. X    (void) fsync(fileno(fp));    /* commit to disk */
  1863. X    (void) fclose(fp);
  1864. X
  1865. X    (void) sigsetmask(omask);    /* restore signal mask */
  1866. X
  1867. X    return 0;
  1868. X}
  1869. X
  1870. X/*
  1871. X**    histtime: return modification time of history file
  1872. X*/
  1873. Xhisttime(histfile, ptime)
  1874. Xchar   *histfile;
  1875. Xtime_t *ptime;
  1876. X{
  1877. X    struct stat    st;
  1878. X
  1879. X    if(stat(histfile, &st) < 0) {
  1880. X        syslog(LOG_NOTICE, "histtime: stat(%s): %m", histfile);
  1881. X        return -1;
  1882. X    }
  1883. X    *ptime = st.st_mtime;
  1884. X
  1885. X    return 0;
  1886. X}
  1887. X
  1888. X/*
  1889. X**    histadd: add a player to the history array
  1890. X*/
  1891. Xstatic history *
  1892. Xhistadd(c)
  1893. Xint        c;
  1894. X{
  1895. X    register history   *ph;
  1896. X    char               *id;
  1897. X    int                    omask;
  1898. X
  1899. X    omask = sigsetmask(~0);
  1900. X
  1901. X    if(nhist == 0)
  1902. X        hist = (history *)malloc(sizeof *hist), ++nhist;
  1903. X    else
  1904. X        hist = (history *)realloc((char *)hist, ++nhist * sizeof *hist);
  1905. X
  1906. X    if(hist == 0) {
  1907. X        syslog(LOG_ERR, "histadd: no memory for more history");
  1908. X        nhist = 0;
  1909. X        (void) sigsetmask(omask);
  1910. X        return (history *)0;
  1911. X    }
  1912. X
  1913. X    /*
  1914. X    **    Initialize the new entry.
  1915. X    */
  1916. X    ph = hist + nhist - 1;
  1917. X    if(*(id = plr[c].p_id) == '\0')
  1918. X        id = plr[c].p_name;
  1919. X    strncpy(ph->h_id, id, IDLEN);
  1920. X    ph->h_id[IDLEN-1] = '\0';
  1921. X    ph->h_computer = compbyname(ph->h_id);
  1922. X    ph->h_rank = nhist, ph->h_weight = 0;
  1923. X    ph->h_points = ph->h_avgturn = ph->h_wins = ph->h_games = 0;
  1924. X    ph->h_mvpoints = ph->h_mvavgturn = ph->h_mvwins = ph->h_mvgames = 0;
  1925. X    ph->h_lastgame = 0;
  1926. X
  1927. X    (void) sigsetmask(omask);
  1928. X
  1929. X    return ph;
  1930. X}
  1931. X
  1932. X/*
  1933. X**    histpoints: add player's points to history
  1934. X**        updates game count and lastgame and adds new entries as needed
  1935. X**        uses a fifty-percent rule to penalize players not onboard
  1936. X*/
  1937. Xhistpoints(c)
  1938. Xint        c;
  1939. X{
  1940. X    history       *ph;
  1941. X    char       *id;
  1942. X    long        scale, fifty;
  1943. X    long        avgturn;
  1944. X    double        winrate;
  1945. X
  1946. X    /*
  1947. X    **    Locate player's entry in history array.
  1948. X    **    If player doesn't have an entry, call histadd
  1949. X    **    to add one.  Histadd returns a pointer to the
  1950. X    **    new entry or zero on error.
  1951. X    */
  1952. X    if(*(id = plr[c].p_id) == '\0')
  1953. X        id = plr[c].p_name;
  1954. X    if((ph = histbyname(id)) == 0) {
  1955. X        if((ph = histadd(c)) == 0) {
  1956. X            syslog(LOG_DEBUG, "histpoints: histadd failed");
  1957. X            return -1;
  1958. X        }
  1959. X    }
  1960. X
  1961. X    /*
  1962. X    **    If this non-COMP player was not onboard, it was probably a quit.
  1963. X    **    In this case, we penalize the player's point total to get the
  1964. X    **    same ptsmode average that would result if the game was played to
  1965. X    **    finish and the player got 50% of its ptsmode average.  We must not
  1966. X    **    update game count or lastgame.  We do adjust the moving average
  1967. X    **    using the same half-average score, though the updating is a
  1968. X    **    bit more complex due to the nature of the recent point total.
  1969. X    */
  1970. X    if(plr[c].p_onboard == False && iscomp(c) == False) {
  1971. X        if(ph->h_games == 0)
  1972. X            return 0;
  1973. X
  1974. X        histcalc(ph, &winrate, &fifty, &avgturn);
  1975. X        fifty /= 2;
  1976. X
  1977. X        /*
  1978. X        **    The next calculation done purely as integer math is likely to
  1979. X        **    overflow once the player has about 500 games.
  1980. X        */
  1981. X#ifdef    notdef
  1982. X        ph->h_points = (ph->h_games * ph->h_points + fifty) / (ph->h_games + 1);
  1983. X#else    notdef
  1984. X        {
  1985. X            double    rat, gam;
  1986. X
  1987. X            gam = (double)(ph->h_games + 1);
  1988. X            rat = (double)ph->h_games / gam;
  1989. X            ph->h_points = (long)(ph->h_points * rat + fifty / gam);
  1990. X        }
  1991. X#endif    notdef
  1992. X
  1993. X        if(ph->h_mvgames == MVGAMES)
  1994. X            ph->h_mvpoints += fifty - ph->h_mvpoints / MVGAMES;
  1995. X        else    /* next line not strictly correct if h_mvgames > MVGAMES */
  1996. X            ph->h_mvpoints = (ph->h_mvgames * ph->h_mvpoints + fifty)
  1997. X                / (ph->h_mvgames + 1);
  1998. X
  1999. X        return 0;
  2000. X    }
  2001. X
  2002. X    /*
  2003. X    **    Update points and avgturn and increment game count.  History points
  2004. X    **    are based on games of WINSCORE points, so scale points here before
  2005. X    **    adding to history.  Note that avgturn does not need scaling.
  2006. X    **    Game count is incremented and lastgame is set only here.  Mvwins
  2007. X    **    is updated here as if the game was a loss -- corrected in histwin.
  2008. X    */
  2009. X    scale = (WINSCORE * (long)plr[c].p_score) / winscore;
  2010. X    avgturn = turnnum > 0 ? (long)plr[c].p_score / turnnum : 0L;
  2011. X
  2012. X    if(ph->h_mvgames == MVGAMES) {
  2013. X        ph->h_mvpoints += scale - ph->h_mvpoints / MVGAMES;
  2014. X        ph->h_mvavgturn += avgturn - ph->h_mvavgturn / MVGAMES;
  2015. X        ph->h_mvwins -= ph->h_mvwins / MVGAMES;
  2016. X    } else if(ph->h_mvgames < MVGAMES) {
  2017. X        ph->h_mvpoints += scale;
  2018. X        ph->h_mvavgturn += avgturn;
  2019. X        /* no adjustment of h_mvwins necessary */
  2020. X        ++ph->h_mvgames;
  2021. X    } else {    /* over! */
  2022. X        ph->h_mvpoints = (MVGAMES * ph->h_mvpoints) / ph->h_mvgames;
  2023. X        ph->h_mvavgturn = (MVGAMES * ph->h_mvavgturn) / ph->h_mvgames;
  2024. X        ph->h_mvwins = (MVGAMES * ph->h_mvwins) / ph->h_mvgames;
  2025. X        ph->h_mvgames = MVGAMES;
  2026. X        ph->h_mvpoints += scale - ph->h_mvpoints / MVGAMES;
  2027. X        ph->h_mvavgturn += avgturn - ph->h_mvavgturn / MVGAMES;
  2028. X        ph->h_mvwins -= ph->h_mvwins / MVGAMES;
  2029. X    }
  2030. X
  2031. X    ph->h_games++;
  2032. X    ph->h_points += scale;
  2033. X    ph->h_avgturn += avgturn;
  2034. X    ph->h_lastgame = gamenum;
  2035. X
  2036. X    return 0;
  2037. X}
  2038. X
  2039. X/*
  2040. X**    histwins: update win count for game winner
  2041. X**        assumes that histpoints has been called to update game count
  2042. X**        assumes that histpoints adjusts mvwins as if game was lost
  2043. X**        doesn't add new history entries as needed
  2044. X*/
  2045. Xhistwins(c)
  2046. Xint        c;
  2047. X{
  2048. X    register char       *id;
  2049. X    register history   *ph;
  2050. X
  2051. X    if(*(id = plr[c].p_id) == '\0')
  2052. X        id = plr[c].p_name;
  2053. X
  2054. X    if((ph = histbyname(id)) == 0) {
  2055. X        syslog(LOG_DEBUG, "histwins: can't find <%s> in history", id);
  2056. X        return -1;
  2057. X    }
  2058. X
  2059. X    ph->h_wins++;
  2060. X    ph->h_mvwins += H_MVWINMULT;
  2061. X    return 0;
  2062. X}
  2063. X
  2064. X/*
  2065. X**    histwgt: return weighted value of history item n
  2066. X*/
  2067. Xlong
  2068. Xhistwgt(n)
  2069. Xint        n;
  2070. X{
  2071. X    register history   *ph;
  2072. X    long                avgpoints, avgturn;
  2073. X    double                winrate;
  2074. X
  2075. X    if(n >= nhist || (ph = hist + n)->h_games == 0) {
  2076. X        ph->h_weight = 0;
  2077. X        return 0L;
  2078. X    }
  2079. X
  2080. X    histcalc(ph, &winrate, &avgpoints, &avgturn);
  2081. X    ph->h_weight = (long)(1000L * winrate) + avgpoints + avgturn;
  2082. X
  2083. X    return ph->h_weight;
  2084. X}
  2085. X
  2086. X/*
  2087. X**    histavgplr: return player's historical average game points
  2088. X*/
  2089. Xlong
  2090. Xhistavgplr(c)
  2091. Xint        c;
  2092. X{
  2093. X    history       *ph;
  2094. X    char       *id;
  2095. X    long        avgmpt, avtnpt;
  2096. X    double        winrt;
  2097. X
  2098. X    if(*(id = plr[c].p_id) == '\0')
  2099. X        id = plr[c].p_name;
  2100. X    
  2101. X    if((ph = histbyname(id)) == 0 || ph->h_games == 0)
  2102. X        return 0L;    /* no average */
  2103. X
  2104. X    histcalc(ph, &winrt, &avgmpt, &avtnpt);
  2105. X    return avgmpt;
  2106. X}
  2107. X
  2108. X/*
  2109. X**    histwgtplr: return player's historical wins/points weighting
  2110. X*/
  2111. Xlong
  2112. Xhistwgtplr(c)
  2113. Xint        c;
  2114. X{
  2115. X    history       *ph;
  2116. X    char       *id;
  2117. X
  2118. X    if(*(id = plr[c].p_id) == '\0')
  2119. X        id = plr[c].p_name;
  2120. X    
  2121. X    if((ph = histbyname(id)) == 0)
  2122. X        return 0L;
  2123. X
  2124. X    return histwgt((int)(ph - hist));
  2125. X}
  2126. X
  2127. X/*
  2128. X**    humanplaying: returns true if the human is playing or watching this game
  2129. X*/
  2130. Xboolean
  2131. Xhumanplaying(c, id)
  2132. Xint        c;
  2133. Xchar   *id;
  2134. X{
  2135. X    register int    cc;
  2136. X
  2137. X    for(cc = 0;cc < PLAYERS;++cc) if(cc != c) {
  2138. X        switch(plr[cc].p_stat) {
  2139. X        case Active:
  2140. X        case Waiting:
  2141. X        case Watching:
  2142. X            if(strncmp(plr[cc].p_id, id, IDLEN) == 0)
  2143. X                return True;
  2144. X            break;
  2145. X        }
  2146. X    }
  2147. X
  2148. X    return False;
  2149. X}
  2150. X
  2151. X/*
  2152. X**    histfmtplr: return formatted history entry for player
  2153. X**        or entry in relation to player
  2154. X**    Note: this routine is used by the server to let players check
  2155. X**        their own and other players' histories during a game.
  2156. X**        Some deception is used when Kamelion is the subject of inquiry.
  2157. X*/
  2158. Xchar *
  2159. Xhistfmtplr(c, rel, useid)
  2160. Xint        c, rel;
  2161. Xboolean    useid;
  2162. X{
  2163. X    register history   *ph;
  2164. X    register int        n, d;
  2165. X    register char       *id;
  2166. X    static char            fmt[MESGLEN];
  2167. X
  2168. X    /*
  2169. X    **    Sorry, but we don't want to have to do a lookup.
  2170. X    **    What's the right thing to do in this case anyway?
  2171. X    */
  2172. X    if(useid == False && rel != 0)
  2173. X        useid = True;
  2174. X
  2175. X    /*
  2176. X    **    We have to let Waiting and Watching pass here, or a such a
  2177. X    **    player can't use the related commands at the "next game?"
  2178. X    **    prompt.  This can be used to spy on observers.
  2179. X    */
  2180. X    if(c < 0 || c >= PLAYERS || plr[c].p_stat == Inactive) {
  2181. X        sprintf(fmt, "No player number %d.", c+1);
  2182. X        return fmt;
  2183. X    }
  2184. X
  2185. X    if(*(id = plr[c].p_id) == '\0')
  2186. X        id = plr[c].p_name;
  2187. X    if((ph = histbyname(id)) == 0) {
  2188. X        sprintf(fmt, "%s is not ranked.",
  2189. X            useid == True ? plr[c].p_id : plr[c].p_name);
  2190. X        return fmt;
  2191. X    }
  2192. X    n = (int)(ph - hist);
  2193. X
  2194. X    if(rel != 0) {
  2195. X        ph += rel, n += rel;
  2196. X        if(n < 0 || n >= nhist) {
  2197. X            sprintf(fmt, "No player is ranked %d.", n+1);
  2198. X            return fmt;
  2199. X        }
  2200. X    }
  2201. X
  2202. X    /*
  2203. X    **    When rel is zero and useid is False, this is a query about one player
  2204. X    **    by another.  In this case, we try to protect Kamelion's identity.
  2205. X    **    What we do is look for the nearest ranked human that's not playing.
  2206. X    **    If there isn't one, we tell the truth.
  2207. X    */
  2208. X    if(rel == 0 && useid == False && iskamelion(c) == True) {
  2209. X        for(d = 1;d < nhist;++d) {
  2210. X            if(n - d >= 0 && (ph-d)->h_computer == 0
  2211. X            && humanplaying(c, (ph-d)->h_id) == False) {
  2212. X                ph -= d, n -= d;
  2213. X                break;
  2214. X            }
  2215. X            if(n + d < nhist && (ph+d)->h_computer == 0
  2216. X            && humanplaying(c, (ph+d)->h_id) == False) {
  2217. X                ph += d, n += d;
  2218. X                break;
  2219. X            }
  2220. X        }
  2221. X    }
  2222. X
  2223. X    if(ph->h_games == 0)
  2224. X        sprintf(fmt, "%ld %.18s 0 * * * %ld",
  2225. X            ph->h_rank, useid == True ? ph->h_id : plr[c].p_name, ph->h_weight);
  2226. X    else {
  2227. X        long    avgpoints, avgturn;
  2228. X        double    winrate;
  2229. X
  2230. X        histcalc(ph, &winrate, &avgpoints, &avgturn);
  2231. X        sprintf(fmt, "%ld %.18s %ld %5.3f %ld %ld %ld",
  2232. X            ph->h_rank, useid == True ? ph->h_id : plr[c].p_name,
  2233. X            ph->h_games, winrate, avgpoints, avgturn, ph->h_weight);
  2234. X    }
  2235. X    
  2236. X    return fmt;
  2237. X}
  2238. X
  2239. X/*
  2240. X**    historder: reorder players by historical wins/points weighting
  2241. X**        and/or score from previous game
  2242. X*/
  2243. Xhistorder(firstgame)
  2244. Xboolean    firstgame;
  2245. X{
  2246. X    register int    c;
  2247. X    int                omask;
  2248. X#define    SORTBONUS    15000
  2249. X
  2250. X    /*
  2251. X    **    Give active players a score based on their history unless they
  2252. X    **    played in the previous game.  In that case, award them a bonus
  2253. X    **    that guarantees preferential treatment.
  2254. X    */
  2255. X    for(c = 0;c < PLAYERS;++c) {
  2256. X        switch(plr[c].p_stat) {
  2257. X        case Computer:
  2258. X        case Active:
  2259. X            if(firstgame == True || plr[c].p_score == 0)
  2260. X                plr[c].p_score = (int)histwgtplr(c);
  2261. X            else
  2262. X                plr[c].p_score += SORTBONUS;
  2263. X            break;
  2264. X        default:
  2265. X            plr[c].p_score = 0;
  2266. X            break;
  2267. X        }
  2268. X    }
  2269. X
  2270. X    omask = sigsetmask(~0);
  2271. X    qsort((char *)plr, PLAYERS, sizeof plr[0], plrcmp);
  2272. X    (void) sigsetmask(omask);
  2273. X
  2274. X#undef    SORTBONUS
  2275. X}
  2276. X
  2277. X/*
  2278. X**    histbyname: return pointer to player history or NULL
  2279. X*/
  2280. Xhistory *
  2281. Xhistbyname(id)
  2282. Xregister char  *id;
  2283. X{
  2284. X    register int    h;
  2285. X
  2286. X    for(h = 0;h < nhist;++h)
  2287. X        if(strncmp(id, (hist+h)->h_id, IDLEN) == 0)
  2288. X            return hist+h;
  2289. X    
  2290. X    return (history *)0;
  2291. X}
  2292. X
  2293. X/*
  2294. X**    setgamenum: set current game number using COMP's game count
  2295. X*/
  2296. Xsetgamenum()
  2297. X{
  2298. X    register int    h;
  2299. X
  2300. X    /*
  2301. X    **    Search for the COMP computer and set the gamenum to one more
  2302. X    **    than the number of games played.  This works because the COMP
  2303. X    **    computer plays in every game.
  2304. X    */
  2305. X    for(h = 0;h < nhist;++h) {
  2306. X        if((hist+h)->h_computer == &comptbl[0]) {
  2307. X            gamenum = (hist+h)->h_games + 1;
  2308. X            return;
  2309. X        }
  2310. X    }
  2311. X
  2312. X    /*
  2313. X    **    This really may be the first game!
  2314. X    */
  2315. X    if(nhist == 0) {
  2316. X        gamenum = 1;
  2317. X        return;
  2318. X    }
  2319. X
  2320. X    /*
  2321. X    **    Don't know what happened to cause this, but let's try to handle it by
  2322. X    **    setting the gamenum to one more than the highest played by any player.
  2323. X    */
  2324. X    syslog(LOG_DEBUG, "setgamenum: can't find COMP computer");
  2325. X    gamenum = 0;
  2326. X    for(h = 0;h < nhist;++h)
  2327. X        if((hist+h)->h_lastgame > gamenum)
  2328. X            gamenum = (hist+h)->h_lastgame;
  2329. X    ++gamenum;
  2330. X}
  2331. X
  2332. X/*
  2333. X**    histcalc: calculate winrt, avgmpt, and avtnpt from history
  2334. X*/
  2335. Xhistcalc(ph, pwinrt, pavgmpt, pavtnpt)
  2336. Xregister history   *ph;
  2337. Xdouble               *pwinrt;
  2338. Xlong               *pavgmpt, *pavtnpt;
  2339. X{
  2340. X    if(ph->h_games == 0) {
  2341. X        *pwinrt = 0.0;
  2342. X        *pavgmpt = *pavtnpt = 0;
  2343. X        return;
  2344. X    }
  2345. X
  2346. X    switch(ptsmode) {
  2347. X    case Lifetime:
  2348. X        *pwinrt = (double)ph->h_wins / ph->h_games;
  2349. X        *pavgmpt = ph->h_points / ph->h_games;
  2350. X        *pavtnpt = ph->h_avgturn / ph->h_games;
  2351. X        break;
  2352. X    case Recent:
  2353. X        *pwinrt = (double)ph->h_mvwins / (H_MVWINMULT * ph->h_mvgames);
  2354. X        *pavgmpt = ph->h_mvpoints / ph->h_mvgames;
  2355. X        *pavtnpt = ph->h_mvavgturn / ph->h_mvgames;
  2356. X        break;
  2357. X    case Combined:
  2358. X        *pwinrt = (double)ph->h_wins / ph->h_games;
  2359. X        *pavgmpt = ph->h_points / ph->h_games;
  2360. X        *pavtnpt = ph->h_avgturn / ph->h_games;
  2361. X        *pwinrt += (double)ph->h_mvwins / (H_MVWINMULT * ph->h_mvgames);
  2362. X        *pavgmpt += ph->h_mvpoints / ph->h_mvgames;
  2363. X        *pavtnpt += ph->h_mvavgturn / ph->h_mvgames;
  2364. X        *pwinrt *= 0.5;
  2365. X        *pavgmpt /= 2;
  2366. X        *pavtnpt /= 2;
  2367. X        break;
  2368. X    }
  2369. X}
  2370. END_OF_FILE
  2371. if test 17309 -ne `wc -c <'history.c'`; then
  2372.     echo shar: \"'history.c'\" unpacked with wrong size!
  2373. fi
  2374. # end of 'history.c'
  2375. fi
  2376. echo shar: End of archive 6 \(of 8\).
  2377. cp /dev/null ark6isdone
  2378. MISSING=""
  2379. for I in 1 2 3 4 5 6 7 8 ; do
  2380.     if test ! -f ark${I}isdone ; then
  2381.     MISSING="${MISSING} ${I}"
  2382.     fi
  2383. done
  2384. if test "${MISSING}" = "" ; then
  2385.     echo You have unpacked all 8 archives.
  2386.     rm -f ark[1-9]isdone
  2387. else
  2388.     echo You still need to unpack the following archives:
  2389.     echo "        " ${MISSING}
  2390. fi
  2391. ##  End of shell archive.
  2392. exit 0
  2393.